#ifndef MEMORYDEVICE_H
#define MEMORYDEVICE_H

    #include "wininclude.h"

    #include <string>
	#include <vector>
	#include <string.h>

    #define MEMORY_READ_FAIL			0x00000001 // cannot read memory
    #define MEMORY_WRITE_FAIL			0x00000010 // cannot write memory

	enum varType {T_BYTE, T_UBYTE, T_SHORT, T_USHORT, T_INT, T_UINT, T_FLOAT,
		T_SZSTRING, T_UNUSED};
	class CVardata
	{
		public:
			CVardata();

			int iData;
			float fData;
			char cData;
			short sData;
			varType type;
			std::string szData;

			unsigned int varcount;
	};

    class MemoryDevice
    {
        protected:
			unsigned int readBatch_parsefmt(char *, std::vector<CVardata> &);

        public:
			template <class T>
			T readMemory(HANDLE process, unsigned long address, int &err)
			{
				T buffer;
				SIZE_T bytesread = 0;
				err = 0;
				int success = 0;

				success = ReadProcessMemory(process, (LPCVOID)address,
				(void *)&buffer, sizeof(T), &bytesread);

				if( success == 0 )
					err = MEMORY_READ_FAIL;

				return buffer;
			}

			template <class T>
			T readMemoryPtr(HANDLE process, unsigned long address, long offset, int &err)
			{
				T buffer = 0;
				SIZE_T bytesread = 0;
				long real_addr = 0;
				int success = 0;
				err = 0;

				success = ReadProcessMemory(process, (LPVOID)address,
				(void*)&real_addr, sizeof(long), &bytesread);

				if( success == 0 ) {
					err = MEMORY_READ_FAIL;
					return 0; }

				success = ReadProcessMemory(process,
				(LPCVOID)(real_addr + offset), (void*)&buffer,
				sizeof(T), &bytesread);

				if( success == 0 )
					err = MEMORY_READ_FAIL;

				return buffer;
			}


			template <class T>
			void writeMemory(HANDLE process, unsigned long address, T data, int &err)
			{
				SIZE_T byteswritten = 0;
				err = 0;
				int success = 0;
                DWORD old;

                VirtualProtectEx(process, (void *)address, sizeof(T), PAGE_READWRITE, &old);
				success = WriteProcessMemory(process, (void *)address,
				(void*)&data, sizeof(T), &byteswritten);
                VirtualProtectEx(process, (void *)address, sizeof(T), old, &old);

				if( success == 0 )
					err = MEMORY_WRITE_FAIL;
			}

			template <class T>
			void writeMemoryPtr(HANDLE process, unsigned long address, long offset,
			T data, int &err)
			{
				long real_addr = 0;
				SIZE_T bytesread = 0;
				SIZE_T byteswritten = 0;
				int success = 0;
				err = 0;
                DWORD old;

				// find the real (base) address to write to
				success = ReadProcessMemory(process, (LPCVOID)address,
				(void*)&real_addr, sizeof(long), &bytesread);

				if( success == 0 ) {
					err = MEMORY_READ_FAIL;
					return; }

				// now write to that address + offset
                VirtualProtectEx(process, (void *)(real_addr + offset), sizeof(T), PAGE_READWRITE, &old);
				success = WriteProcessMemory(process,
				(void *)(real_addr + offset), (void*)&data,
				sizeof(T), &byteswritten);
                VirtualProtectEx(process, (void *)(real_addr + offset), sizeof(T), old, &old);

				if( success == 0 )
					err = MEMORY_WRITE_FAIL;
			}

            std::string readString(HANDLE, unsigned long, int &, unsigned int = 0);
            std::wstring readUString(HANDLE, unsigned long, int &, unsigned int = 0);
            std::string readStringPtr(HANDLE, unsigned long, long, int &, unsigned int = 0);
            std::wstring readUStringPtr(HANDLE, unsigned long, long, int &, unsigned int = 0);

			void writeString(HANDLE, unsigned long, char *, int &, unsigned int = 0);
			void writeStringPtr(HANDLE, unsigned long, long, char *, int &, unsigned int = 0);

			void readBatch(HANDLE, unsigned long, char *, std::vector<CVardata> &, int &);
    };

#endif
